#ifndef __EXPORTMESH_H__
#define __EXPORTMESH_H__

#include "Skeleton.h"
#include "MeshVertex.h"
#include <string>
#include <sstream>
#include <fstream>

class ExportMesh
{
public:

	enum
	{
		RET_OK = 0,
		RET_ERROR,
		RET_NOT_MESH,
		RET_NO_TEXCOORDS
	};

	ExportMesh() {}

	int Build( IGameObject* obj, IGameNode* node )
	{
		if ( !obj || !node )
			return RET_ERROR;
		if ( obj->GetIGameType() != IGameMesh::IGAME_MESH )
			return RET_NOT_MESH;

		m_Name				= node->GetName();
		IGameMesh* mesh		= static_cast<IGameMesh *>(obj);
		const int vertCount	= mesh->GetNumberOfVerts();
		const int faceCount	= mesh->GetNumberOfFaces();
		Tab<int> texMaps	= mesh->GetActiveMapChannelNum();
		int i, j;

		m_Matrix = node->GetObjectTM( 0 );

		if ( texMaps.Count() <= 0 )
			return RET_NO_TEXCOORDS;

		//opimization for allocation
		m_IndexList.resize(faceCount * 3);
		m_VertexList.Prepare(vertCount);

		for ( i = 0; i < faceCount; ++i )
		{
			FaceEx* face = mesh->GetFace( i );

			for ( j = 0; j < 3; ++j )
			{
				Point3 p = mesh->GetVertex( face->vert[ j ] );

				MeshVertex v( p.x, p.y, p.z );
				Point3 n = mesh->GetNormal( face, j );
				v.SetNormal( n.x, n.y, n.z );
				v.SetID( face->vert[ j ] );

				Point3 uvw;
				DWORD indices[3];

				if ( mesh->GetMapFaceIndex( texMaps[ 0 ], i, indices ) )
					uvw = mesh->GetMapVertex( texMaps[ 0 ], indices[ j ] );
				else
					uvw = mesh->GetMapVertex( texMaps[ 0 ], face->vert[ j ] );

				v.SetTexCoord( uvw.x, 1.0f - uvw.y );
				m_IndexList[ 3 * i + j ] = m_VertexList.Add( v );
			}
		}

		if ( -1 == m_Matrix.Parity() )
		{
			std::vector<int> newInds;
			newInds.resize( m_IndexList.size() );
			for ( i = 0; i < (int)m_IndexList.size() / 3; ++i )
			{
				newInds[ 3 * i + 0 ] = m_IndexList[ 3 * i + 2 ];
				newInds[ 3 * i + 1 ] = m_IndexList[ 3 * i + 1 ];
				newInds[ 3 * i + 2 ] = m_IndexList[ 3 * i + 0 ];
			}

			m_IndexList.swap( newInds );
		}

		return RET_OK;
	}

	int CaptureVertexWeights( IGameObject* obj, Skeleton* skeleton )
	{
		IGameSkin* skin = obj->GetIGameSkin();
		if ( !skin )
			return FALSE;
		if ( IGameSkin::IGAME_SKIN != skin->GetSkinType() )
			return FALSE;

		const int numVerts = this->GetNumVertexes();
		int i, j, numW, vID;

		for ( i = 0; i < numVerts; ++i )
		{
			vID = this->GetVertex( i ).GetID();
			numW = skin->GetNumberOfBones( vID );

			if ( IGameSkin::IGAME_RIGID == skin->GetVertexType( vID ) )
				numW = 1;

			for ( j = 0; j < numW; ++j )
			{
				float w = skin->GetWeight( vID, j );
				if ( numW == 1 )
					w = 1.0f;
				int boneId = skeleton->GetBoneIDByMaxID( skin->GetBoneID( vID, j ) );
				this->GetVertex( i ).AddWeight( VertexWeight( w, boneId ) );
			}
		}

		return TRUE;
	}

	template<typename T>
	void Write(std::ostream& ss, const T& value)
	{
		ss.write(reinterpret_cast<const char *>(&value), sizeof(value));
	}

	template<>
	void Write<std::string>(std::ostream& ss, const std::string& value)
	{
		unsigned short size = value.length();
		Write(ss, size);
		ss.write(value.c_str(), size);
	}

	void WriteToStream(std::ostream& ss)
	{
		unsigned short version = 1;

		Write(ss, version);

		Write(ss, GetName());

		unsigned short indexCount = GetNumIndexes();

		unsigned short triCount = indexCount / 3;
		Write(ss, triCount); 

		for (int i = 0; i < indexCount; i += 3 )
		{
			Write(ss , (unsigned short)GetIndex(i));
			Write(ss , (unsigned short)GetIndex(i+1));
			Write(ss , (unsigned short)GetIndex(i+2));
		}

		unsigned short vertCount = this->GetNumVertexes();
		Write(ss , vertCount);

		for (int i = 0; i < vertCount; ++i)
		{
			MeshVertex& v = GetVertex(i);
			Write(ss , v.GetPosition().x);
			Write(ss , v.GetPosition().y);
			Write(ss , v.GetPosition().z);

			Write(ss , v.GetNormal().x);
			Write(ss , v.GetNormal().y);
			Write(ss , v.GetNormal().z);

			Write(ss , v.GetNormal().x);
			Write(ss , v.GetNormal().y);
			Write(ss , v.GetNormal().z);

			Write(ss , v.GetTexCoord().x);
			Write(ss , v.GetTexCoord().y);
			Write(ss , (float)1.0);
			
			//ss , v.GetNumWeights() , std::endl;
			//for (int j = 0; j < v.GetNumWeights(); ++j )
			//	ss , v.GetWeight( j ).GetBoneID() , " " , v.GetWeight( j ).GetWeight() , std::endl;
		}
	}


	const std::string& GetName( void ) const
	{
		return m_Name;
	}

	int GetNumVertexes( void ) const
	{
		return m_VertexList.Count();
	}

	int GetNumIndexes( void ) const
	{
		return (int)m_IndexList.size();
	}

	MeshVertex& GetVertex( int i )
	{
		return m_VertexList[ i ];
	}

	int GetIndex( int i ) const
	{
		return m_IndexList[ i ];
	}

	const GMatrix& GetMatrix( void ) const
	{
		return m_Matrix;
	}

	void Clear( void )
	{
		m_Name = "";
		m_VertexList.Clear();
		m_IndexList.clear();
	}

private:

	std::string				m_Name;
	MeshVertexList			m_VertexList;
	std::vector<int>		m_IndexList;
	GMatrix					m_Matrix;
};

#endif	//__EXPORTMESH_H__

